package com.md.util;

import com.md.value.constants.Constants;
import com.md.value.constants.MatchConstants;
import lombok.experimental.UtilityClass;
import org.apache.commons.validator.routines.UrlValidator;

import java.math.BigDecimal;


/**
 * 校验工具类
 *
 * @author 刘骄阳
 * 2021-09-17 23:26
 */
@UtilityClass
public class MatchUtil {
  private static final UrlValidator URL_VALIDATOR = new UrlValidator();

  /**
   * 校验是否是数字
   *
   * @param number 要判断的字符
   * @return 判断结果
   */
  public static boolean isNumber(final String number) {
    if (number.matches(MatchConstants.IS_NUMBER)) {
      // 负整数或正整数
      return true;
    } else if (number.matches("^-?\\d+(\\.\\d+)?$")) {
      // 负小数或正小数
      return true;
    } else {
      try {
        new BigDecimal(number);
        return true;
      } catch (NumberFormatException e) {
        // 如果抛出NumberFormatException异常，说明字符串无法转换为数字
        return false;
      }
    }
  }

  /**
   * 校验是否是手机号
   *
   * @param phone 要判断的字符
   * @return 判断结果
   */
  public static Boolean isPhone(final String phone) {
    return MatchConstants.IS_PHONE.matcher(phone).matches();
  }

  /**
   * 校验是否是验证码
   *
   * @param email 要判断的字符
   * @return 判断结果
   */
  public static Boolean isEmail(final String email) {
    return MatchConstants.IS_EMAIL.matcher(email).matches();
  }

  /**
   * 校验是否是身份证
   *
   * @param authCode 要判断的字符
   * @return 判断结果
   */
  public static Boolean isAuthCode(final String authCode) {
    return MatchConstants.IS_AUTH_CODE.matcher(authCode).matches();
  }

  /**
   * 校验身份证是否合规
   * https://zhidao.baidu.com/question/429803039.html
   *
   * @param authCode 身份证
   * @return 判断结果
   */
  public static Boolean isTrueAuthCode(final String authCode) {
    //（1）前1、2位数字表示：所在省份的代码;
    //（2）第3、4位数字表示：所在城市的代码;
    //（3）第5、6位数字表示：所在区县的代码;
    //（4）第7~14位数字表示：出生年、月、日（7.8.9.10位是年,11.12位是月,13.14位是日）;
    //（5）第15、16位数字表示：所在地的派出所的代码;
    //（6）第17位数字表示：性别奇数表示男性,偶数表示女性;
    //（7）第18位数字是：校检码校检码可以是0~9的数字,有时也用x表示.
    //   身份证第18位（校验码）的计算方法
    //　　1、将前面的身份证号码17位数分别乘以不同的系数.从第一位到第十七位的系数分别为：7－9－10－5－8－4－2－1－6－3－7－9－10－5－8－4－2.
    //　　2、将这17位数字和系数相乘的结果相加.用加出来和除以11
    //　　4、余数只可能有0－1－2－3－4－5－6－7－8－9－10这11个数字.其分别对应的最后一位身份证的号码为1－0－X －9－8－7－6－5－4－3－2 .
    if (Boolean.TRUE.equals(isAuthCode(authCode))) {
      String[] code = authCode.split("");
      int sum = 0;
      for (int i = 0; i < code.length - 1; i++) {
        //将i对应的数字乘以i对应的加权因子,结果相加
        sum += Integer.parseInt(code[i]) * Constants.AUTH_CODE_WEIGHTING_FACTOR[i];
      }
      //再余11
      int sign = Math.floorMod(sum, 11);
      //判断最后一位身份证号是否是对应的权值
      return code[code.length - 1].equals(Constants.AUTH_CODE_SIGN[sign]);
    }
    return false;
  }

  /**
   * 校验是否正常的url
   *
   * @return true: 正常; false: 异常
   */
  public Boolean isNormalURL(final String url) {
    return ObjectUtil.notEmpty(url) && URL_VALIDATOR.isValid(url);
  }

  /**
   * 校验异常是否是指定异常数组的子集
   *
   * @param ex                  异常
   * @param retryableExceptions 异常数组
   * @return 是否子集(true : 是 ; false : 否)
   */
  public static boolean isRetryableException(final Exception ex,
                                             final Class<? extends Exception>[] retryableExceptions) {
    for (Class<? extends Exception> clazz : retryableExceptions) {
      if (clazz.isInstance(ex)) {
        return true;
      }
    }
    return false;
  }

  /**
   * 判断是否是中文
   *
   * @param word 字符
   * @return true: 是; false: 否
   */
  public static boolean isChinese(final char word) {
    return Character.toString(word).matches("\\p{InCJK Unified Ideographs}");
  }
}
